Skip to content

chore(pm): the queue guard reads its record recognisers from record-recognisers.mjs; check-clause2-carriers.mjs is deleted - #19803

Merged
hotlong merged 5 commits into
mainfrom
claude/issue-19061-step4-record-recognisers-module
Sep 23, 2026
Merged

hotlong merged 5 commits into
mainfrom
claude/issue-19061-step4-record-recognisers-module

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #19061

维护者速读(草稿)

  • 改了什么:队列守卫(Governed Surface Queue Guard)放行 .claude/** 时读 ## Contract review 记录所用的识别器,以及席位照抄的 --template 记录模板,从 10,220 行的 scripts/pm/check-clause2-carriers.mjs 原样搬进新文件 scripts/pm/record-recognisers.mjs(连同钉住它们的自测用例),然后删除旧文件。守卫改为懒加载新文件;lint.yml 里原先跑旧文件自测的那一步改指新文件(不是新增门禁);contract-review.md 两处 --template 指针改写为新命令。
  • 为什么改:裁决 B(三层归一)的最后一步。前三步已落地;旧文件只剩守卫一处懒加载在用,其余(--pair 扫描、C1–C9 行、认领读取器)早已退役、无人调用。
  • 风险与代价(含回滚):守卫的判定不变 —— 守卫自测改前改后都是 279 例全过;搬过去的识别器由新文件自测 110 例和守卫自测两头钉住。回滚 = revert 本 PR。代价:本 PR 改动 11,684 行(+1,392 / −10,292),超过 5,000 行的人工合并线。

席位意见

建议批准并合并;这是裁决 B(三层归一)的最后一步,做的全是减法。 一万行的 check-clause2-carriers.mjs 删掉,队列守卫懒加载的那几个记录识别器和 --template 原样搬进 1,324 行的 record-recognisers.mjs(含它们自己的 110 个自测例);守卫指向新文件,懒加载没动(dev 用消融证明一改成即时加载就死锁,所以懒加载必须留);--template 输出 40 行里后 39 行逐字节相同,只有第一行改名。lint 工作流那一步是改指向不是新增,步数不变;contract-review.md 两处指针跟着改,行数 24 → 26,天花板 60。席位在新 worktree 上重跑:识别器自测 110、守卫自测 279、半态自测 4897、十个门禁全 0。之所以要你点合并:改动行数 11,684 超过 5,000 的人工合并线(删除也计数),不是档位问题。合并即 Fixes #19061,四步全部落地。复核记录:PR 评论 5790479344(PASS,在席按服务档渲染);ACCEPT 在卡 #19061。CI 读数:36 latest-per-name check runs — 30 success, 6 skipped, 0 in progress, 0 other。

  • 你要做的:席位贴出当前 head 的 ## Contract review PASS 记录、所有检查全绿之后,请您手动点合并 —— 超 5,000 行的 PR 按规则只能人工合并,队列守卫的 size 腿会拒绝入队。

Clause-②: no

What this does

Ruling record 5770886272 (letter B), its last step, named on Release 5789425558. Four commits, one per class, so a reviewer can bisect:

  1. a4a16863e — the module. scripts/pm/record-recognisers.mjs holds exactly what check-governed-queue-guard.mjs reads from the old file — through loadRecordRecognisers (locateReviewOfRecord, deliveredCardNumber, servedTierStands, isModelIdentifierToken, REVIEW_OF_RECORD_THREADS, REVIEW_OF_RECORD_LOCATION) and in its self-test through RECOGNISER_SOURCES.tier (CONTRACT_REVIEW_TIER_NAME, unexplainedPathsBetween) — plus what those are built from (readServedTier, the stamp control, the regeneration carry, REVIEWED_BY_LINE, DELIVERY_EVIDENCE_PRECEDENCE) and the record template (contractReviewTemplateLines, contractReviewRecordLines, RECORD_TEMPLATE_PLACEHOLDERS, the two fences) with a --template CLI. The code blocks are verbatim slices of the old file's blob at 2cf9db7c4; ten docblock sentences that named a deleted function as live are corrected, each listed under Deviations. The self-test cases that pin these readers moved with them (110 cases, six batteries, roster floor and verdict handshake); the cases that read the retired rows did not.
  2. baa17b116 — the re-points. The guard's RECOGNISER_SOURCES.tier is ./record-recognisers.mjs (still a LAZY import from a function body), its renamed-source pin and docblocks follow. The lint.yml step that ran the old self-test is retargeted to pnpm check:pm-record-recognisers — ⛔ not a gate added beside it. references/contract-review.md :13 / :19 now point at node scripts/pm/record-recognisers.mjs --template. Present-tense prose that named the old file as a live importer, location or message is re-pointed (list below).
  3. 81b3469d6 — the deletion. scripts/pm/check-clause2-carriers.mjs (10,220 lines) is removed, and with it, not re-homed: the --pair scan and its readers (derivePairs, C1–C9, pairRows, the read-path ladder, the input record), the claim readers no live importer used, the local copy of the retired label constant, and the self-test battery of those parts.
  4. Two merges of origin/main (the last at 98d83361d); neither touches a file this PR edits.

Measured premises

  • H1 holds. At 2cf9db7c4 the guard's RECOGNISER_SOURCES.tier read ./check-clause2-carriers.mjs. git grep for a statement-shaped import/from of the file: zero hits. With the file deleted in the worktree before any re-point, 27 scripts/pm/*.mjs --self-test runs (every one but dispatch-gates.mjs, whose battery takes about 12 minutes, and check-dispatch-gates.mjs) plus check-governed-merges.mjs --test and first-party-closure.mjs were compared with the same runs with the file present: the ONLY exit that moved was the guard's self-test, 0 → 1 (ERR_MODULE_NOT_FOUND). Both omitted batteries passed on the final tree (Tests). The one other consumer was CI's own invocation (lint.yml:1267 through the check:pm-clause2-carriers alias), retargeted here.
  • H2 — first half holds, second half is falsified. The guard's self-test passes 279/279 before and after, with the renamed-source pin updated. The cycle was re-measured through the new file by ablation: a module-scope import of record-recognisers.mjs planted in the guard exits 13 with Detected unsettled top-level await at .../check-half-states.mjs:9765; restored byte-identical (blob a2fd67afc6bb == HEAD, git diff HEAD empty). But a fresh node -e import of the new module DOES load the guard: it imports check-half-states.mjs for the record readers and deliveryEvidence, and that file's module-scope top-level await imports the guard — exactly as the old file did. Avoiding it would mean restating check-half-states.mjs' readers, which the module's own rule forbids; this is why the guard's import stays lazy.
  • H3. See Tests. check:first-party-closure does not exist: scripts/first-party-closure.mjs is a library with no CLI; its consumers' self-tests ran (check-adr-0087-registration 441 assertions, check:objectui-changeset).
  • H4. No overlap: origin/main moved 2cf9db7c4 → 98d83361d during this work and touched none of these files.
  • H5. 11,684 changed lines (+1,392 / −10,292, 12 files) — OVER the 5,000 human-merge threshold. The deletion was not split to dodge it.

--template, before and after

40 lines both times; lines 2–40 byte-identical. Line 1 names the tool that prints it — record-recognisers --template instead of the deleted check-clause2-carriers --template.

Tests — at HEAD 5c24980ef

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) derived 85 commands at 5c24980ef, byte-identical to the list derived at bfe5b9917 before the last merge; all 85 were run on 5c24980ef, each exit captured before any pipe, and --ran answers ✓ dispatch-gates --ran: 85 derived famil(ies) accounted for — 82 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3).
  • NOT MEASURED, declared — five families that read BUILT dist/ output, with no workspace package built here (74 of 78 build tasks are cache misses): check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt exit 3 PREREQUISITE NOT MET; check:dts-closure and check:sourcemap-no-sources-content exit 0 but swept only the 4 packages built for check:doc-formula-expressions (formula, lint and their closure), a partial population and not a measurement of the whole. This diff changes no workspace package's source, manifest or build — only the private root package's scripts map — so none of their verdicts can move; CI builds the closure and runs them.
  • pnpm check:pm-record-recognisers → ✓ record-recognisers self-test: 110 cases pass. Ablations through scripts/ablation-replace.mjs, each landed on disk and restored to the HEAD blob: servedStampsHold made to accept any control → 3 of 110 red; DELIVERY_EVIDENCE_PRECEDENCE reordered → 2 of 110 red.
  • node scripts/pm/check-governed-queue-guard.mjs --self-test → ✓ check-governed-queue-guard self-test: 279 cases pass (279 before the change too).
  • pnpm check:pm-dispatch-gates → ✓ dispatch-gates self-test: 1905 cases pass. · pnpm check:pm-half-states → 4897 cases · pnpm check:pm-widening-tells → 525 · pnpm check:pm-expected-skips → 99 · pnpm check:closing-target-claim → 105 · check-changeset-no-major --self-test → 339 · check-adr-0087-registration --self-test → 441 · check-self-test-wired → every one of the 229 script(s) CI runs that ship a --self-test has that self-test run by CI · pnpm check:pm-skill-ratchet → contract-review.md is 26 lines (ceiling 60; headroom 34), every line at most 120 bytes (measured widest 118) · pnpm check:skill-frame-sync, pnpm check:doc-authoring, pnpm check:declared-population-live, pnpm check:nul-bytes, check:doc-formula-expressions (after building formula and lint under the verify lock) → exit 0.
  • ESLint, a proven narrowing: the population is eslint.config.mjs's files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'] object minus NEVER_LINTED, which covers scripts/**; eslint --no-inline-config --format json over the 8 changed .mjs files reports 8 files, 0 errors, 0 warnings; the config enables no type-aware linting (--print-config shows parserOptions of ecmaVersion and sourceType only), so the diff cannot move the verdict of any file it does not touch.
  • node scripts/pm/check-governed-merges.mjs --test over the three-dot path list → GOVERNED, Tier S (contract-review.md is the one register hit).

Deviations

  • The module has its own --self-test, and the lint step is RETARGETED, not removed. The moved docblocks cite pins ("the self-test drives THIS function's output", "a MIRROR, so the self-test MEASURES it", "the cross-tool pin in this file's self-test"); moving the docblocks without those pins would have left them false, and the kept readers would have lost their unit cases. What moved is only cases that exercise a function this file owns.
  • --template line 1 names the new tool (above) — the dispatch asked for the old output exactly; a banner naming a deleted file is the one line changed.
  • references/contract-review.md grows 24 → 26 lines: each re-pointed line is wrapped once, because the new spelling would push both past the 120-byte line cap (they were 119 and 104 bytes). No other line's text changed.
  • scripts/first-party-closure.mjs :21 is unchanged: it is past-tense narration of the [finding] An accept-set narrowing owes a **BREAKING** banner in core but not in platform-objects — and the ADR-0087 classifier reads the banner #16421 measurement, not a closure list — the closure is computed from import edges, so the deletion needs no edit there.
  • Docblock corrections inside the moved code (each the one sentence that named a deleted function or constant as live): the regeneration hop's printable shape, the carry memo's reason, the value-decoration strip, the authorship reader comparison, the CLAUSE2_CORRECTION_KEY_LINE aside, the repo-shape example "below", the template round-trip reader, the derivePairs / H31 askers, the reviewOfRecord "below", and the template banner. REVIEWED_BY_LINE is now keyLineRegex('Reviewed-by') (the same regex the deleted AUTHORSHIP_KEY_LINES built; pinned equal by source in the self-test).
  • Prose re-pointed outside the named list: scripts/pm/clause2-line.mjs (its docblock described this very step as pending) and one runtime usage line in scripts/pm/check-widening-tells.mjs (--repo refusal named the deleted file as the convention's reader; it now names check-half-states.mjs). In check-half-states.mjs one runtime sentence drops its clause about the deleted reader.

Acceptance notes

  • scripts/pm/check-widening-tells.mjs' docblock still narrates its only caller, the deleted --pair (the C5 row, an exit register "pinned in THAT file's self-test", the caller section). Left as history rather than rewritten; the tool now has no caller besides its own self-test. carrier: 承接者:无.
  • History left as history: check-skill-line-ratchet.mjs :437 / :483 (raise ledger), dispatch-gates.mjs :6799 (an import-edge measurement), check-changeset-no-major.mjs :1452 (a phrase attribution), check-half-states.mjs :1404 / :2740 / :24659, the two docs/audits/ census snapshots.
  • The template's calibration notes still describe retired mechanics — "refused HALF WRITTEN" (the retired C4 row) and "FAIL strips the two carriers" (the retired carrier discipline). Kept byte-identical because seats paste this output and the dispatch pinned it.
  • Exports left with no importer by the deletion: CLAUSE2_VALUES / CLAUSE2_ARMS in clause2-line.mjs, bindingClosesCard in check-half-states.mjs. Not touched.
  • Pre-existing, unrelated, identical before and after this diff: node scripts/pm/measurement-claim-triage.mjs --self-test exits 1 on origin/main (UNTRIAGED scripts/check-dts-references.mjs:74); it is not wired into CI.

Generated by Claude Code


Generated by Claude Code

…, moved out of check-clause2-carriers

Step 4 of ruling record 5770886272 (letter B). The readers the queue guard's
Tier S leg imports — locateReviewOfRecord, deliveredCardNumber,
servedTierStands, isModelIdentifierToken, REVIEW_OF_RECORD_THREADS /
REVIEW_OF_RECORD_LOCATION, CONTRACT_REVIEW_TIER_NAME, unexplainedPathsBetween —
and the record template with its --template CLI, moved verbatim with their
docblocks into their own module. The self-test cases that pin these readers
move with them; the ones that read the retired rows do not.

Nothing imports this file yet; the guard is re-pointed in the next commit.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
…ointers at record-recognisers.mjs

- check-governed-queue-guard.mjs: RECOGNISER_SOURCES.tier is now
  './record-recognisers.mjs' (still a LAZY import; the no-top-level-await pin
  and the renamed-source pin follow), and its docblocks name the new file.
- lint.yml + package.json: the step that ran check-clause2-carriers' self-test
  is retargeted, not added beside — `pnpm check:pm-record-recognisers`.
- references/contract-review.md: the two `--template` pointers name
  `node scripts/pm/record-recognisers.mjs --template`, each wrapped once to
  stay under the 120-byte line cap (24 -> 26 of 60 lines).
- Present-tense prose that named the old file as a live importer, location or
  message is re-pointed: clause2-line, check-expected-skips,
  check-closing-target-claim, check-adr-0087-registration, check-half-states
  (one runtime sentence included), check-widening-tells (its usage refusal).

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
…g record 5770886272

Nothing imports it any more: the queue guard's lazy import reads
record-recognisers.mjs, and the lint step runs that file's self-test. Gone with
it, not re-homed: the retired `--pair` scan and its readers (derivePairs, the
C1-C9 rows, pairRows, the read-path ladder, the input record), the claim
readers no live importer used, the local copy of the retired label constant,
and the self-test battery of those parts.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation labels Sep 23, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 23, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5c24980efc02f62946cc48c5cac979212d269e87

① Derived judgments

Rendered in-seat by the skills seat 1 session at the served tier the maintainer named (ruling record 5771798588; 「契约复审继续用 fable 不变」 5788482806 of this date). The contract is ruling B on #19061 (5770886272, 三层归一), step ④ as named on the step-③ Release 5789425558 and the claim 5789522248: the record recognisers and --template leave scripts/pm/check-clause2-carriers.mjs into one small module the queue guard imports, then the file is deleted with the retired --pair scan and the label constant; the two --template pointers in references/contract-review.md follow; ⛔ no gate added, ⛔ no change to what the guard decides.

  • (a) The diff — measured by this seat on a review worktree at the head. Five commits on merge-base 0b83e0162 (the module a4a16863e, the re-points baa17b116, the deletion 81b3469d6, two merges of origin/main); 12 files, +1392 / −10292 = 11,684 changed lines. scripts/pm/check-clause2-carriers.mjs (10,220 lines) is gone; scripts/pm/record-recognisers.mjs (1,324 lines) carries exactly what check-governed-queue-guard.mjs lazy-imports — locateReviewOfRecord, deliveredCardNumber, servedTierStands, isModelIdentifierToken, REVIEW_OF_RECORD_THREADS / REVIEW_OF_RECORD_LOCATION, CONTRACT_REVIEW_TIER_NAME, unexplainedPathsBetween, the record template and a --template CLI — with the 110 self-test cases that pin those readers; the guard's RECOGNISER_SOURCES.tier at :659 now reads ./record-recognisers.mjs, the lazy import untouched (the dev's ablation planted an eager import and got exit 13 「unsettled top-level await」 — the cycle the :622–:669 docblock describes still exists through check-half-states.mjs, so the laziness stays load-bearing, as the dispatch said it must).
  • (b) The --template output, compared by this seat. 40 lines from the old file on origin/main and 40 from the new module; lines 2–40 byte-identical, line 1 names record-recognisers --template instead of the old file — the copyable record of record is unchanged.
  • (c) The pointers and the retargets. contract-review.md :13 / :19 → :13–:14 / :20–:21, each wrapped once under the 120-byte cap (24 → 26 lines, ceiling 60, check:pm-skill-ratchet 0); .github/workflows/lint.yml :1250–:1263 — the self-test step is RETARGETED to pnpm check:pm-record-recognisers, step count unchanged (the moved pins need their CI runner; not an added gate); package.json :85 the script renamed; prose re-points in clause2-line.mjs, check-expected-skips.mjs, check-closing-target-claim.mjs, check-adr-0087-registration.mjs, check-half-states.mjs, check-widening-tells.mjs (each a sentence that would otherwise name a deleted file as live). Residual mentions of the old name on the head (the seat's git grep: lint.yml 1 comment, check-half-states.mjs 6, check-widening-tells.mjs 11 docblock lines, check-skill-line-ratchet.mjs 2, dispatch-gates.mjs 1, clause2-line.mjs 2, first-party-closure.mjs 1 narration, two audit docs) are history, none a mechanism — and the closure list the dispatch named at first-party-closure.mjs :21 is narration of a past measurement, correctly left.
  • (d) The deletion is safe by measurement, not by claim. The dev's deletion leg ran 27 scripts/pm self-tests plus check-governed-merges --test with the file present and deleted before any re-point: only the guard's self-test moved (0 → 1 ERR_MODULE_NOT_FOUND) — the guard was the only live importer, as the step-③ review had read. Exports the deletion orphaned (CLAUSE2_VALUES / CLAUSE2_ARMS in clause2-line.mjs, bindingClosesCard in check-half-states.mjs) are dead exports, noted, not widened into this PR.
  • (e) Gates — re-run by this seat on the review worktree at the head after pnpm install --frozen-lockfile --offline. check:pm-record-recognisers 0 (110 cases); check-governed-queue-guard.mjs --self-test 0 (279, unchanged); check-half-states.mjs --self-test 0 (4897); check:pm-skill-ratchet 0; check:skill-frame-sync 0; check:doc-authoring 0; check:pm-skill-id-lint 0; check:pm-governed-prose 0; check-self-test-wired.mjs 0 (229 scripts wired — the lint step retarget keeps the moved self-test run by CI; the seat's first attempt through the pnpm wrapper returned an empty exit 254 and the direct run is the reading); check:entry-guard 0; check:pm-expected-skips 0; check-governed-merges.mjs --pr 19803 exit 3 — GOVERNED Tier S (.claude/** ×1: contract-review.md) and HUMAN MERGE (11,684 changed lines over the 5,000 line, generated files included, the 2026-09-18 ruling — the same terminal as a Tier H diff). The dev's derivation: 85 families by --commands, --ran 82 / 85 / 3 NOT-MEASURED (dist-reading gates on an unbuilt workspace, no package touched), the 1905-case dispatch-gates self-test, ablations on servedStampsHold (3 red) and DELIVERY_EVIDENCE_PRECEDENCE (2 red) restored clean. CI at the head at the seat's read: 34 latest-per-name check runs — 21 success, 6 skipped, 7 in progress; the 四件套 waits for green.

② Semver level

None — no release. scripts/pm/**, .claude/**, .github/** and the private root package.json are in no package's files[]; skip-changeset written through label-write.mjs with read-back (the dev's report).

③ Boundary flags

  • HUMAN MERGE by size (the register's size limb, not a tier): the seat does not ready, enqueue or arm auto-merge; on green the 四件套 — ACCEPT on the card, needs-user-decision on the PR, 席位意见 in the body, review requested from GOVERNED_APPROVERS — and the maintainer clicks merge; Fixes #19061 closes the card at that merge, ruling B complete.
  • Deviations, all correct readings: the lint step retargeted rather than removed (above); --template line 1 renamed; first-party-closure.mjs :21 left as narration; two files re-pointed beyond the dispatch's list (clause2-line.mjs, check-widening-tells.mjs) — pointers, in scope; the dispatch's check:first-party-closure gate name was this seat's error (no such gate; the library's consumers' self-tests ran).
  • Report 5790420237 on the card parses; mcp_calls 0; three REST writes through the fleet-write relay plus five pushes; no body PATCH by the dev; the worktree removed by the dev after the PR opened.
  • Out of scope, noted by the dev, ⛔ not touched: measurement-claim-triage.mjs --self-test exits 1 on origin/main itself (an untriaged magnitude in check-dts-references.mjs :74; not wired into CI) — class a, a filing this seat's identity cannot make → the maintainer's items; check-widening-tells.mjs's docblock narrates the deleted caller (history, 11 sites); the template's calibration notes describe retired mechanics — the seat pinned those bytes and re-spells them in its own later PR; the dead exports above.

Implemented-by: claude/issue-19061-step4-record-recognisers-module
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Standing down on the queue refusal — this PR lands only by 人工直合, not through the merge queue. domain:skills seat 1 (session_01Wnstp2kTth7sGXfr8fXypc, seat post #7623), 2026-09-23T07:50Z. ⛔ Not a code failure; no patch round is owed and the seat pushes nothing.

What was measured (REST + the job log, read this act):

The one door this PR has — as the guard's own remedy text states it and as #19344 (maintainer item 6) narrowed it: the Merge button's bypass-rules option (「Merge without waiting for requirements to be met」), offered only while ruleset main lists a bypass actor (the Repository-admin role or the GOVERNED_APPROVERS accounts). PR #19024 landed exactly this way on 2026-09-20 (merged_by os-zhuang). ⛔ Every re-enqueue comes back to this same refusal: the size limb is not lifted by an APPROVED review, by the seat's ## Contract review PASS (5790479344, head 5c24980ef), or by green CI — all three already stand on this head.

Seat position: the 四件套 is complete (ACCEPT 5790743219 on #19061, needs-user-decision, the 席位意见 block in this body, review requested from os-zhuang / hotlong). The PR stays ready and out of the queue with auto-merge off (the dequeue disarmed it); the seat neither re-drafts it nor re-arms anything. ⭐ Ask of hotlong / os-zhuang: land it by the bypass-rules merge (squash), or tell the seat to park it as draft. The seat confirms the landing by content on origin/main (scripts/pm/record-recognisers.mjs present, check-clause2-carriers.mjs absent) and then closes out #19061 (Fixes).

@hotlong
hotlong merged commit 6eaa0f4 into main Sep 23, 2026
53 checks passed
@hotlong
hotlong deleted the claude/issue-19061-step4-record-recognisers-module branch September 23, 2026 08:14
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…H / Tier S (objectstack-ai#19820)

Fixes objectstack-ai#19146

Clause-②: no

## What this PR does

Re-keys the four remaining PM-charter lines that still spelled the
retired 规则层 / 事实层 two-layer split to the register's own two tiers —
**Tier S** (every governed path under `.claude/**`; the owning seat
lands it through the queue on a `## Contract review` PASS record) and
**Tier H** (every other governed surface; draft until an authorized
APPROVED review) — exactly as `.claude/agents/os-dev.md` :286–:287
already spells them since PR objectstack-ai#19802 (`4a12ac0c4`). The wording model is
ruling B on objectstack-ai#19061 (comment 5770886272) as `os-dev.md` carries it; this
delivery completes the remainder named by the seat's Release 5790724014
on the card. Vocabulary only: each rule's meaning is unchanged, and no
line is added or removed in any file.

### The four lines, before and after (bytes against the 120-byte cap)

1. `.claude/skills/pm-dispatch/SKILL.md` :187 (118 B → 116 B)
   - before 「- 路径面命中规则层 ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。」
- after 「- 路径面命中 Tier H ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。」
- 规则层 here meant a Tier H path: one Tier H path hit switches ACCEPT to
the terminal four-set; the mixed-diff clause is untouched.
2. `.claude/skills/pm-dispatch/SKILL.md` :188 (111 B → 110 B)
- before 「- 改动 >5000 行(含生成物)同换终局四件套,⛔ 无事实层例外;读数 = PR
additions+deletions。」
- after 「- 改动 >5000 行(含生成物)同换终局四件套,⛔ 无 Tier S 例外;读数 = PR
additions+deletions。」
- 事实层 here meant Tier S: the size limb has NO Tier S exception — the
rule the queue guard applied to PR objectstack-ai#19803 today; only the word moved.
3. `.claude/skills/pm-dispatch/references/landing-operations.md` :58
(117 B → 115 B)
- before 「- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>5000
行(含生成物)照规则层。」
- after 「- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>5000
行(含生成物)照 Tier H。」
- 规则层 here meant the Tier H terminal (四件套, a human merge); :59–:60
beside it already spell Tier S / Tier H.
4. `.claude/skills/pm-dispatch/references/lanes/skills.md` :17 (113 B →
111 B)
- before 「- 规则层受管 PR 恒 draft 等维护者的字;flip 或 arm 前跑 `check-governed-merges
--test` 带路径。」
- after 「- Tier H 受管 PR 恒 draft 等维护者的字;flip 或 arm 前跑
`check-governed-merges --test` 带路径。」

Line counts unchanged: SKILL.md 319 / 319 (headroom 0),
landing-operations.md 101 / 101 (headroom 0), lanes/skills.md 32 / 33
(headroom 1). Widest edited line 116 B. The edit was applied by an
anchor-counted replace (each old line asserted to occur exactly once,
each new line read back exactly once, line count asserted equal), not by
a regex tool's exit code.

### Premise and concurrency, measured

- H1 on `origin/main` `6eaa0f4a8`: `git grep -n -E '规则层|事实层' origin/main
-- .claude/skills/pm-dispatch/ .claude/agents/` → exactly the four lines
above, nothing else. After the edit: 0 hits on that surface, and the
card's own acceptance grep (`grep -rn 事实层 .claude AGENTS.md`) → 0 hits.
- The only remaining whole-tree hits are
`scripts/pm/check-governed-queue-guard.mjs` :236 / :240, inside a
verbatim maintainer-ruling quotation that the file's own header marks as
reproduced rather than paraphrased — not a spelling of the retired
split, not on this surface, not edited.
- H3 at PR-open time: all 17 open PRs' file lists read over REST (PR
objectstack-ai#17076's 773 / 773 files paged to a short page) — none touches any of
the three files.
- Register: `node scripts/pm/check-governed-merges.mjs --test` on the
three paths → `GOVERNED — Tier S(席内达档复核落地)`, exit 3.

### Landing — Tier S

`.claude/**` is on the register as Tier S. This PR is opened as a DRAFT
and stays draft: the owning seat renders the `## Contract review` record
for the current head and lands it through the queue once every check is
green. No seat approves it and no maintainer click is waited for. objectstack-ai#19803
is not addressed here; it is cited only as the guard's reading of the
size limb.

### Gates at head `1282c7efc`

`1282c7efc` = `8f7e56a6c` (the four-line commit) merged with
`origin/main` `0e06f3b5d`; the three-dot delta vs `origin/main` is
exactly the three files, +4 −4.

`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` (no paths; changeset from the merge base) derived 20
families, the same list at `8f7e56a6c` and at `1282c7efc`. Every command
was run on `1282c7efc` with its exit captured before any pipe
(stdout+stderr redirected to a file, the exit read on the next
statement): 19 × exit 0, 1 × exit 3. Reconciliation `--ran` → `20
derived famil(ies) accounted for — 19 run, 1 NOT-MEASURED (1 DERIVED
from a recorded exit 3)`, exit 0.

- `check:pm-skill-ratchet` exit 0 — `.claude/skills/pm-dispatch/SKILL.md
is 319 lines (ceiling 319; headroom 0)`, `widest table row is 342 bytes
(pin 342; headroom 0)`; `references/landing-operations.md is 101 lines
(ceiling 101; headroom 0)`; `references/lanes/skills.md is 32 lines
(ceiling 33; headroom 1)`. Same readings before the edit at `6eaa0f4a8`.
- `check:skill-frame-sync` exit 0 — the one declared copy of the
decision frame is internally coherent, 4 axes; the frame block (SKILL.md
:241–:261) is untouched.
- `check:pm-skill-id-lint` exit 0 (34 files clean) · `check:nul-bytes`
exit 0 · `check:doc-authoring` exit 0 (16192 customer-facing strings
clean) · `check:pm-governed-prose` exit 0 (2 surfaces name all 6
registered governed surfaces) · `check:pm-expected-skips` exit 0 (99
cases).
- `check:pm-governed-merges` exit 0 (441 assertions) ·
`check-governed-queue-guard.mjs --self-test` exit 0 (279 cases) ·
`check-harness-current.mjs --self-test` exit 0 (26 cases) ·
`check-closing-keyword-parity.mjs` self-test 40 assertions, sweep OK (3
parsers agree on all 9 keywords) · `check:watch-hint-literal` exit 0 (71
declarations) · `check:gitlink-declared` exit 0 (36 assertions).
- `check-comment-mask-corpus.mjs` exit 0 (7020 files, 0 disagree) ·
`check:agent-test-spelling` exit 0 (0 violations) ·
`check:cross-package-test-inputs` exit 0 (255 self-test cases; 29
packages, all declared) · `check:driver-memory-census` exit 0 ·
`check:refd-timer-probe` exit 0 (11 cases).
- NOT MEASURED: `pnpm --filter @objectstack/lint run
check:doc-formula-expressions` exit 3 = `PREREQUISITE NOT MET — the
workspace package @objectstack/formula is not built`. This diff touches
no package, so no ① build closure is owed, and that family's population
(docs formula expressions) is disjoint from the three paths.

No package is touched: no package build or test, no verify-lock slot
taken. `skills/**` (the published catalog) is untouched, so no
whole-file / whole-package token readings are owed. No changeset:
`.claude/**` publishes nothing from any package's `files[]` ⇒
`skip-changeset`.

## 维护者速读(草稿)

- **改了什么**:PM 派发技能(`pm-dispatch`)里还剩四行沿用已废弃的「规则层 / 事实层」两层叫法(SKILL.md
两行、落地操作一行、skills 车道一行),本 PR 把它们改成登记表现行的 Tier H / Tier S 词汇,与 `os-dev.md`
已落地的写法一致。只换词,不换规则:每条规则的意思、每个文件的行数都不变。
- **为什么改**:每个席位每轮都读这几行;词汇与登记表、AGENTS.md 第 14 条不一致,席位会按一个已不存在的分层去判断 PR
该怎么落地——例如把「无事实层例外」读成一条无人能对上号的例外。
- **风险与代价(含回滚)**:零功能改动,不触任何发布包;派生门禁 20 族 19 绿、1 未测(formula 包未构建,与本 diff
无关,CI 会跑)。回滚即还原四行,无迁移、无数据影响。
- **席位意见**:(待席位填写)
- **你要做的**:无需操作;`.claude/**` 属 Tier S,由席位在契约复核 PASS 记录上入队落地。

## Acceptance notes

- noted, not filed: `scripts/pm/check-governed-queue-guard.mjs` :236 /
:240 still carry 事实层 / 规则层 inside the verbatim maintainer-ruling
quotation the file reproduces on purpose — immutable, and not a live
instance of the retired split.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…y the gate-family selector (objectstack-ai#19812)

Fixes objectstack-ai#19753

Clause-②: no

## Ruling (not reopened here)

The maintainer's 「同意 A′」, recorded by triage in comment 5790385022 on
objectstack-ai#19753: `check:migration-registry` becomes a step inside the required
`Lint & Repo Gates` job. It runs only when a change touches
`packages/spec/src/migrations/**`, and it hangs on the existing
gate-family selector. It is not a separate `paths:`-filtered required
check, which would never report on unrelated PRs.

## Design

- **`scripts/ci/select-gate-families.sh`**: adds the new family
`migration_registry`. It comes first in `FAMILIES` because its step is
the earliest scoped step in the job. Its read-set is everything the gate
reads:
- `packages/spec/src/migrations/**`: the entries and the generated
`registry.ts`. An edit to either side alone is the drift the gate
reports.
  - the generator, `packages/spec/scripts/build-migration-registry.ts`.
- `packages/spec/package.json`, which pnpm resolves the script through.
- `packages/spec/tsconfig.json`, which tsx loads. It extends the root
tsconfig, and root config already runs every family.

Every other path skips. A skip here is a ratchet-grade claim: no changed
path is one the gate reads. The selector's fail-open branches still run
this family like every other one. Those branches are: an unscoped event
(push on main, the hourly run), a missing or unresolvable base, a failed
or empty diff, an unclassified path, and a deletion or rename.
- **`.github/workflows/lint.yml`**: adds one step, `Migration registry
matches its entry files`, with `if:
steps.gate-families.outputs.migration_registry != 'skip'` and `run: pnpm
--filter @objectstack/spec check:migration-registry`. It sits right
after `ADR anchors` because the job's ordering note puts cheap gates
first. The selector step's comment now counts TEN families. The region
PR objectstack-ai#19803 edits (hunk at 1247) is untouched.
- **`scripts/ci/select-gate-families.selftest.sh`**:
  - The fixture gains the read-set and its nearest neighbours.
- 12 new cases: entry M/A/D, a stray non-TS file in an entry dir,
`registry.ts`, the generator, the spec manifest and tsconfig, a sibling
spec script, a spec source outside the tree, a lookalike directory, and
a pull_request feature branch.
- Every count line that moved is updated. The one `expect_all_run` that
became false (`scripts/helper.mjs`) now lists the nine families it runs.
  - A new `pin_step` for the new step.
  - The floor rises from 42 cases / 220 checks to 56 / 293.
- **Rosters**: none needed updating. Only the selector, its self-test
and `lint.yml` name family ids. `check:migration-registry` was already
classified in `packages/spec/scripts/check-generated.ts`.

## On `main` first (ruling item 3)

`pnpm --filter @objectstack/spec check:migration-registry` at
`a5afe382ba` exited 0 with `src/migrations/registry.ts is current (232
semantic, 207 retired-key, 183 retired-def)`. The registry is **not
stale**, so `registry.ts` is untouched and this PR has no changeset:
nothing published changes.

## Duration (ruling item 4)

Wall clock of the exact step command, in a 4-core container:
- 1.37 s when the machine was idle.
- 1.47 to 1.62 s over 5 runs at load ~6.

It needs no build: tsx reads one directory and splices text. When a PR's
diff is outside the read-set, the step is skipped and costs about 0 s.

## Verification (head `e1825aabbf`)

- **Selector self-test**: base `a5afe382ba` passes 44 cases / 228
checks. Head passes 56 cases / 293 checks.
- **Selector on the real tree** (scratch clone, `merge_group`):
- An entry edit gives `run migration_registry reads
packages/spec/src/migrations/entries/semantic/17.action-descriptor-is-async-retired.ts
(M, workspace)`.
- A docs-only commit gives `skip migration_registry no changed path is
in its read-set`.
- An unresolvable base gives `run migration_registry merge group base
... unresolvable`.
- **Lit test of the gate** (scratch clone):
  1. On the clean tree it exits 0.
2. With one entry's prose edited and the registry not regenerated, it
exits 1: `registry.ts is stale`. The edit had landed: in the entry, the
old text went from 1 copy to 0 and the new text from 0 to 1, while the
registry still carried 0 copies of the new text.
  3. After `gen:migration-registry` rewrote one line, it exits 0.
- **Ablations** (scratch clone; each one restored with the blob equal to
HEAD and `git diff HEAD` empty):
  - Remove the migration-tree arm: 16 FAIL.
  - Spell the `if:` as `== 'run'`: 4 FAIL.
  - Run base's selector and workflow under head's self-test: 139 FAIL.
  - Drop `--filter` from the step: the pin FAILs.
- **Derived gates** (`dispatch-gates --repo objectstack-ai/objectstack
--commands`, 51 commands) plus `check:issue-citations` (self-test and
diff-scoped) plus the gate itself:
- All exited 0 except `pnpm check:type-check-debt`, which exited 3 with
`PREREQUISITE NOT MET` (it needs the whole `packages/*` closure built).
That result is NOT MEASURED, not red. The diff touches no package source
or tsconfig, so it is left to CI.
- `pnpm check:pm-dispatch-gates` was still running when this PR opened.
Its verdict is in the `os-dev-report` on objectstack-ai#19753.
- **Hygiene**: 0 control bytes and 0 model identifiers in the added
lines. Both scans fire on a lit control.
- **This PR's own `Lint & Repo Gates` run**: the new step should read
**skip**. This diff touches only `.github/workflows/` and `scripts/ci/`,
and neither is in the read-set. So the first CI execution of the step on
the real tree is the push-on-main run after merge, where every family
runs. The exact command was run locally, and the self-test pins it.

## Acceptance notes

- Noted, not filed: AGENTS.md "Touched `packages/spec`?" says every spec
generated-artifact gate lives in `TypeScript Type Check`. That was
already untrue for this gate, which ran nowhere, and after this PR it
lives in `Lint & Repo Gates`, scoped. AGENTS.md is a governed surface.
Owner: none.
- Landing: this touches `.github/workflows/**`, so per triage, seat
auto-merge answers 422 and the PR needs a human merge.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation needs-user-decision size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retire the in-seat clause-② contract review — the rule, its gate label, and the 9k-line checker's gate role

2 participants